package com.auto.api.common.utils;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
 * @program:
 * @description: id生成器
 * @author: liangshf
 * @create: 2022-01-05
 */
public class IdGenerator {
    private static final Logger log = LoggerFactory.getLogger(IdGenerator.class);

    /**
     * 随机生成器
     **/
    private static volatile ThreadLocalRandom random = ThreadLocalRandom.current();

    /**
     * 起始时间戳 2020-04-09 00:00:00
     */
    public static long EPOCH = 1586361600000L;

    /**
     * worker id bit数 最大值32
     */
    private static final long workerIdBits = 5;

    /**
     * 数据中心标识数
     */
    private static final long dataCenterIdBits = 5;

    /**
     * 序列号位数
     */
    private static final long sequenceBits = 12;

    /**
     * 机器ID偏左移12位
     */
    private static final long workerIdShift = sequenceBits;

    /**
     * 数据中心ID左移17位(12+5)
     */
    private static final long dataCenterIdShift = sequenceBits + workerIdBits;

    /**
     * 时间戳左移22位
     */
    private static final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    /**
     * 序列号最大值 4096
     * -1 的补码(二进制都是1) 左移12位，然后取反
     */
    private static final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 机器ID最大数31
     */
    private static final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 数据标识id 最大31
     */
    private static final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);

    private static final long workerMaxk = -1L ^ (-1L << workerIdBits);
    /**
     * 最大进程值
     */
    private static final long processMaxk = -1L ^ (-1L << dataCenterIdBits);

    /**
     * 工作机器ID(0-31)
     *
     */
    private static long workerId;

    /**
     * 数据中心ID(0-31)
     */
    private long dataCenterId;
    /**
     * 毫秒生产的序列号
     */
    private long sequence = 0L;

    /**
     * 上次执行时间
     */
    private long lastTimestamp = -1L;

    private long extension = 0;

    private long maxExtension = 0;
    /**
     * 保留workerId 和lastTimestamp ,以及备用workerId和其对应的lastTimestamp
     */
    private static Map<Long, Long> worrkerIdLastTimeMap = new ConcurrentHashMap<>();

    /**
     * 最大容人时间，单位毫秒，如果时间回拨了，那么等待相应时间即可，此值不宜过大
     */
    private static final long MAX_BACKWARD_MS = 3;

    private static IdGenerator idWorker;

    static {
        idWorker = new IdGenerator();
    }

    /**
     * 构造函数
     */
    public IdGenerator() {
        this.dataCenterId = getDataCenterId(maxDataCenterId);
        this.workerId = getWorkerId(dataCenterId, maxWorkerId);
    }

    /**
     * 构造函数
     * @param workerId 工作ID (0~31)
     * @param dataCenterId 数据中心ID (0~31)
     */
    public IdGenerator(long workerId, long dataCenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }

    private static class SnowFlakeIdGenHolder {
        private static final IdGenerator instance = new IdGenerator();
    }

    public static IdGenerator getInstance() {
        return SnowFlakeIdGenHolder.instance;
    }

    public static String nextStrId() {
        return String.valueOf(idWorker.generateId());
    }

    public static long nextId() {
        return idWorker.generateId();
    }

    /**
     * 获取下一个ID 线程安全
     * 使用Synchronized控制并发，而非CAS的方式，因为CAS不适合并发量非常高的场景
     * 考虑时钟回拨,如果连续两次时钟回拨，可能还有问题，但是这种概率极低
     * @return
     */
    public synchronized long generateId() {
        long currentTimestamp = timeGen();
        if (currentTimestamp < lastTimestamp) {
            //发生时钟回拨
            long offset = lastTimestamp - currentTimestamp;
            if (offset > MAX_BACKWARD_MS) {
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - currentTimestamp));
            }
            try {
                LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(offset));
                currentTimestamp = timeGen();
                if (currentTimestamp < lastTimestamp) {
                    throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - currentTimestamp));
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        if (currentTimestamp == lastTimestamp) {
            //序列号+1,且不能超过最大序列号
            sequence = (sequence +1) & sequenceMask;
            if (sequence == 0) {
                //超过最大序列号自旋等待到下一毫秒
                currentTimestamp = waitUnitNextTime(lastTimestamp);
            }
        } else {
            //跨毫秒时，序列号总是归0，会导致序列号为0的ID比较多，导致生成的ID取模后不均匀，所以采用10以内的随机数
            sequence = random.nextInt(10) & sequenceMask;
        }
        //更新上次生成id的时间戳
        lastTimestamp = currentTimestamp;
        /*if (log.isDebugEnabled()) {
            log.debug("{}-{}-{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(lastTimestamp)), workerId, sequence);
        }*/
        long timestamp = (currentTimestamp - EPOCH) << timestampLeftShift;
        long workerId = this.workerId << workerIdShift;
        long dataCenterId = this.dataCenterId << dataCenterIdShift;
        return timestamp | dataCenterId | workerId | sequence;
    }

    protected long waitUnitNextTime(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp =timeGen();
        }
        return timestamp;
    }

    public long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     *  获取WorkerId
     * @param dataCenterId
     * @param maxWorkerId
     * @return
     */
    protected static long getWorkerId(long dataCenterId, long maxWorkerId) {
        StringBuffer mpid = new StringBuffer();
        mpid.append(dataCenterId);
        String name = ManagementFactory.getRuntimeMXBean().getName();
        if (!name.isEmpty()) {
            // GET jvmPid
            mpid.append(name.split("@")[0]);
        }
        // MAC + PID 的 hashcode 获取16个低位
        return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
    }

    /**
     * 获取机器编码 用来做数据ID
     * 数据标识id部分 通常不建议采用下面的MAC地址方式，
     * 因为用户通过破解很容易拿到MAC进行破坏
     */
    protected static long getDataCenterId(long tempMaxDataCenterId) {
        if (tempMaxDataCenterId < 0L  || tempMaxDataCenterId > maxDataCenterId) {
            tempMaxDataCenterId = maxDataCenterId;
        }
        long id = 0L;
        try {
            InetAddress ip = InetAddress.getLocalHost();
            NetworkInterface network = NetworkInterface.getByInetAddress(ip);
            if (network == null) {
                id = 1L;
            } else {
                byte[] mac = network.getHardwareAddress();
                id = ((0x000000FF & (long) mac[mac.length - 1])
                    | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
                id = id % (tempMaxDataCenterId + 1);
            }
        } catch (Exception e) {
            System.out.println(" getDatacenterId: " + e.getMessage());
        }
        return id;
    }
}